package org.bouncycastle.cms.gm;

import com.study.crypto.basic.utils.SM2Utils;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.AsymmetricKeyWrapper;
import org.bouncycastle.operator.GenericKey;
import org.bouncycastle.operator.OperatorException;

import java.io.IOException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

/**
 * @author Songjin
 * @since 2021-10-03 17:05
 */
public class GmAsymmetricKeyWrapper extends AsymmetricKeyWrapper {
    
    private PublicKey publicKey;
    private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
    
    public GmAsymmetricKeyWrapper(PublicKey publicKey) throws CMSException {
        super(new AlgorithmIdentifier(GMObjectIdentifiers.sm2encrypt, DERNull.INSTANCE));
        this.publicKey = publicKey;
        if (!(publicKey instanceof BCECPublicKey)) {
            throw new CMSException("非sm2证书，公钥参数不合法");
        }
    }
    
    public GmAsymmetricKeyWrapper(X509Certificate certificate) throws CMSException {
        this(certificate.getPublicKey());
        setProvider(BouncyCastleProvider.PROVIDER_NAME);
    }
    
    /**
     * Create a wrapper, overriding the algorithm type that is stored in the public key.
     *
     * @param algorithmIdentifier identifier for encryption algorithm to be used.
     * @param publicKey           the public key to be used.
     */
    public GmAsymmetricKeyWrapper(AlgorithmIdentifier algorithmIdentifier, PublicKey publicKey) {
        super(algorithmIdentifier);
        
        this.publicKey = publicKey;
    }
    
    protected GmAsymmetricKeyWrapper(AlgorithmIdentifier algorithmId) {
        super(algorithmId);
    }
    
    @Override
    public byte[] generateWrappedKey(GenericKey encryptionKey) throws OperatorException {
        byte[] representation = (byte[]) encryptionKey.getRepresentation();
        try {
            return SM2Utils.encrypt(publicKey, representation, SM2Engine.Mode.C1C3C2);
        } catch (InvalidCipherTextException | IOException e) {
            throw new OperatorException("执行对称密钥加密异常", e);
        }
    }
    
    public GmAsymmetricKeyWrapper setProvider(Provider provider) {
        this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
        return this;
    }
    
    public GmAsymmetricKeyWrapper setProvider(String providerName) {
        this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
        return this;
    }
}
